/*================================================================
*   Copyright (C) 2021 IMVS Ltd. All rights reserved.
*   
*   Filename   ：cantool.c
*   Creator    ：Chase Wu
*   Create Date：2021/06/03
*   Description：
*
================================================================*/

#include "cantool.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

static int cantool_send_data(cantool_t *cantool)
{
	int ret = 0;
	/* configure can data length */
	cantool->frame.can_dlc = 8;
	printf("%s ID=%#x data length=%d\n", cantool->ifr.ifr_name, cantool->frame.can_id, cantool->frame.can_dlc);
	
	/* Sending data */
	ret = write(cantool->cansocket, &cantool->frame, sizeof(cantool->frame));
	if(ret < 0) {
		perror("Send failed");
		return ret;
	}
	printf("Sent successfully\n");

	return ret;
}

static int cantool_receive_data(cantool_t *cantool)
{
	int ret = 0;
	int nbytes;

	/* set filter for only receiving packet */
	cantool->rfilter[0].can_mask = CAN_SFF_MASK;
	ret = setsockopt(cantool->cansocket, SOL_CAN_RAW, CAN_RAW_FILTER, &cantool->rfilter, sizeof(cantool->rfilter));
	if(ret < 0) {
		perror("set receiving filter error\n");
		return ret;
	}
	/* reading... */
	while(1){
		nbytes = read(cantool->cansocket, &cantool->frame, sizeof(cantool->frame));
		if(nbytes > 0) {
			printf("%s ID=%#x data length=%d\n", cantool->ifr.ifr_name, cantool->frame.can_id, cantool->frame.can_dlc);
			for (int i = 0; i < cantool->frame.can_dlc; i++)
				printf("%#x ", cantool->frame.data[i]);
			printf("\n");
		}
	}

	return ret;
}

static int cantool_init(cantool_t *cantool)
{
	int ret = 0;

	/* create socket */
	if ((cantool->cansocket = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
	    perror("Create socket failed");
	    return -1;
	}

	/* set up can interface */
	strcpy(cantool->ifr.ifr_name, cantool->dev);
	printf("can port is %s\n", cantool->ifr.ifr_name);
	/* assign can device */
	ioctl(cantool->cansocket, SIOCGIFINDEX, &cantool->ifr);
    cantool->addr.can_family = AF_CAN;
	cantool->addr.can_ifindex = cantool->ifr.ifr_ifindex;
    /* bind can device */
	ret = bind(cantool->cansocket, (struct sockaddr *)&cantool->addr, sizeof(cantool->addr));
	if(ret < 0) {
		perror("Bind can device failed\n");
		return ret;
	}

	/* set can id 0x1F */
	cantool->frame.can_id = 0x1f;
	cantool->rfilter[0].can_id = 0x1F;

	return ret;
}

static int cantool_release(cantool_t *cantool)
{
	return close(cantool->cansocket);
}

static cantool_op_t cantool_op = {
    .init = cantool_init,
    .release = cantool_release,

    .send = cantool_send_data,
    .receive = cantool_receive_data,
};

int cantool_destory(cantool_t *cantool)
{
	return cantool->op->release(cantool);
}

int cantool_create(cantool_t **pcantool, char *dev)
{
	int ret = 0;

    cantool_t* ptmp;
    (*pcantool) = (cantool_t*)malloc(sizeof(cantool_t));
    if(!(*pcantool)){
        printf("cantool malloc error\n");
        return -1;//-ENOMEM;
    }
    ptmp = *pcantool;
    memset(ptmp,0,sizeof(cantool_t));
    ptmp->op = &cantool_op;
	ptmp->dev = dev;
    ret = ptmp->op->init(ptmp);
    if(ret < 0){
        printf("cantool init error\n");
        cantool_destory(ptmp);
        return ret;
    }
	return ret;
}

int cantool_process(char *dev, int mode)
{
	int ret = 0;
	cantool_t *cantool;

	ret = cantool_create(&cantool, dev);
	if(ret < 0){
		return -1;
	}

	switch(mode){
		case CANTOOL_RECV_MODE:{
			ret = cantool->op->receive(cantool);
		}break;
		case CANTOOL_SEND_MODE:{
			/* prepare data for sending: 0x11,0x22...0x88 */
			for (int i = 0; i < 8; i++) {
				cantool->frame.data[i] = ((i + 1) << 4) | (i + 1);
				printf("%#x ", cantool->frame.data[i]);
			}
			printf("\n");
			ret = cantool->op->send(cantool);
		}break;
		default:{
			printf("wrong parameter input\n");
		}break;
	}

	ret = cantool_destory(cantool);

	return ret;
}